/* udp.c */

#include <string.h>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#include "udp.h"

int udp_open(int port)
{
  int sock;
  struct sockaddr_in addr;

  sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (sock < 0)
    return -1;

  if (port >= 0) {
    memset((char *) &addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
      close(sock);
      return -1;
    }
  }

  return sock;
}

int udp_register_multicast(int sock, net_u32 ip)
{
  struct ip_mreq mreq;

  mreq.imr_multiaddr.s_addr = htonl(ip);
  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    return -1;
  
  return 0;
}

int udp_send(int sock, void *data, int len, net_u32 ip, int port)
{
  struct sockaddr_in dest;

  memset(&dest, 0, sizeof(dest));
  dest.sin_family = AF_INET;
  dest.sin_port = htons(port);
  dest.sin_addr.s_addr = htonl(ip);

  return sendto(sock, data, len, 0, (struct sockaddr *) &dest, sizeof(dest));
}

int udp_recv(int sock, void *data, int len, net_u32 *ip, int *port)
{
  struct sockaddr_in src;
  socklen_t addr_len;

  memset(&src, 0, sizeof(src));
  addr_len = sizeof(src);
  len = recvfrom(sock, data, len, 0, (struct sockaddr *) &src, &addr_len);

  if (ip)
    *ip = ntohl(src.sin_addr.s_addr);
  if (port)
    *port = ntohs(src.sin_port);
  return len;
}

void udp_close(int sock)
{
  close(sock);
}
